﻿#region Copyright ©2008-2011, Technische Universitaet Muenchen

// ====================================================================================================
//
// Last Changed by       $Author: then $
// Last Changed Date     $LastChangedDate: 2009-05-16 14:58:56 +0200 (Sat, 16 May 2009) $
// Last Changed Revision $Rev: 144 $
//
// ====================================================================================================

#endregion

#region

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Xml;
using Tum.CollabXT.TFS.Resources;

#endregion

namespace Tum.CollabXT.TFS
{
    internal class WorkItems : IProcessor
    {
        private const string MilestoneTypeName = "milestone";
        private const string ActivityTypeName = "activity";
        private const string TaskTypeName = "task";
        private const string ArtifactTypeName = "artifact";

        /// <summary>
        /// Name of the field type for constant values.
        /// </summary>
        private const string TypeFieldConst = "const";


        private readonly List<string> IterationPathList = new List<string>();

        public string TaskFileFolder
        {
            get;
            set;
        }

        public string TaskFileName
        {
            get;
            set;
        }


        TemplateProcessor TplProcessor;

        /// <summary>
        /// Found milestone types. Used in ModifyTypeDefinitionFile_AllowedValues_AllMilestoneTypes.
        /// </summary>
        HashSet<string> MilestoneTypes;


        public bool Process(TemplateProcessor processor)
        {
            TplProcessor = processor;

            processor.ToolProvider.ProcessLog.AddEntry(Language.Processing_WorkItems);
            var workItemTemplateFileName = processor.ToolProvider.OutputPath + "/" + TaskFileFolder + "/" + TaskFileName;

            #region Load template

            var workItemDoc = new XmlDocument();
            processor.ToolProvider.ProcessLog.AddEntry(Language.WorkItems_Loading);
            try
            {
                workItemDoc.Load(workItemTemplateFileName);
            }
            catch
            {
                processor.ToolProvider.ProcessLog.AddEntry(string.Format(Language.Error_LoadingFailed, workItemTemplateFileName), LogEntryType.Error);
                return false;
            }

            #endregion

            processor.ToolProvider.ProcessLog.AddEntry(Language.WorkItems_SelectDefaultNode);
            var defaultWorkItemsNode = workItemDoc.SelectSingleNode("/tasks/task[@plugin=\"Microsoft.ProjectCreationWizard.WorkItemTracking\"]/taskXml/WORKITEMS");
            if (defaultWorkItemsNode == null)
            {
                processor.ToolProvider.ProcessLog.AddEntry(string.Format(Language.Error_NotFound_InvalidTemplate, workItemTemplateFileName + ": /tasks/task[@plugin=\"Microsoft.ProjectCreationWizard.WorkItemTracking\"]/taskXml/WORKITEMS"), LogEntryType.Error);
                return false;
            }

            try
            {
                //Write work item instances
                WriteMilestoneInstances(defaultWorkItemsNode);

                WriteActivityInstances(defaultWorkItemsNode);

                WriteTaskInstances(defaultWorkItemsNode);

                WriteArtifactInstances(defaultWorkItemsNode);

                //Modify type definitions
                ModifyTypeDefinitionFiles();
            }
            catch (WorkItemConversionException e)
            {
                //Write the exception message as error
                processor.ToolProvider.ProcessLog.AddEntry(e.Message, LogEntryType.Error);
                return false;
            }

            #region Save work items file

            processor.ToolProvider.ProcessLog.AddEntry(Language.WorkItems_Saving);
            try
            {
                workItemDoc.Save(workItemTemplateFileName);
            }
            catch
            {
                processor.ToolProvider.ProcessLog.AddEntry(string.Format(Language.Error_SavingFailed, workItemTemplateFileName), LogEntryType.Error);
                return false;
            }

            #endregion

            return true;
        }

        private static void AddFieldToXmlNode(XmlNode node, string fieldName, string fieldValue)
        {
            var subNodeTitle = node.OwnerDocument.CreateNode("element", "FIELD", "");
            var refnameAttribute = node.OwnerDocument.CreateAttribute("refname");
            refnameAttribute.Value = fieldName;
            subNodeTitle.Attributes.Append(refnameAttribute);
            var valueAttribute = node.OwnerDocument.CreateAttribute("value");
            valueAttribute.Value = fieldValue;
            subNodeTitle.Attributes.Append(valueAttribute);
            node.AppendChild(subNodeTitle);
        }

        /// <summary>
        /// Writes all default milestone instances.
        /// </summary>
        private void WriteMilestoneInstances(XmlNode wiInstancesNode)
        {
            var typeName = GetWorkItemTypeName(MilestoneTypeName);
            var typeFields = GetWorkItemTypeFields(MilestoneTypeName);
            var typeConsts = GetWorkItemConstFields(MilestoneTypeName);

            //Insert seperator comment
            wiInstancesNode.AppendChild(wiInstancesNode.OwnerDocument.CreateComment("Milestones"));

            //Create hashset for milestone types
            MilestoneTypes = new HashSet<string>();
            
            //Sort milestones for predecessor / successor relations
            SortedList<int, string> milestoneOrderList = new SortedList<int, string>();
            foreach (IMilestone curMilestone in TplProcessor.ProcessProvider.Milestones)
            {
                int scheduleNumber = curMilestone.ScheduleNumber;
                while(milestoneOrderList.ContainsKey(scheduleNumber))
                    scheduleNumber++;
                curMilestone.ExtendedData["TFS_WI_ScheduleNumber"] = scheduleNumber;
                milestoneOrderList.Add(scheduleNumber, curMilestone.Id);
            }

            //Write
            foreach (IMilestone curMilestone in TplProcessor.ProcessProvider.Milestones)
            {
                try
                {
                    TplProcessor.ToolProvider.ProcessLog.AddEntry(string.Format(Language.WorkItems_WritingDecisionGate, curMilestone.Name));

                    //Create default work item node
                    var newWorkItemNode = wiInstancesNode.OwnerDocument.CreateNode("element", "WI", string.Empty);

                    var typeAttribute = wiInstancesNode.OwnerDocument.CreateAttribute("type");
                    typeAttribute.Value = typeName;
                    newWorkItemNode.Attributes.Append(typeAttribute);
                    //--------------

                    //Write attributes
                    WriteCommonWIAttributes(curMilestone, newWorkItemNode, typeFields, typeConsts);

                    #region Write iteration path

                    object devPhase;
                    if (curMilestone.ExtendedData.TryGetValue("DevPhase", out devPhase))
                    {
                        if (devPhase is int)
                        {
                            var iterationPathStr = string.Format("{0:D2} ", (int)devPhase);
                            switch ((int)devPhase)
                            {
                                case 0:
                                    iterationPathStr += TplProcessor.LanguageDictionary["IterationPath_Initiation"];
                                    break;

                                case 99:
                                    iterationPathStr += TplProcessor.LanguageDictionary["IterationPath_Closing"];
                                    break;

                                default:
                                    iterationPathStr += TplProcessor.LanguageDictionary["IterationPath_Execution"];
                                    break;
                            }

                            object devIteration;
                            if (curMilestone.ExtendedData.TryGetValue("DevIteration", out devIteration))
                            {
                                if (devIteration is int)
                                    iterationPathStr += string.Format("\\" + TplProcessor.LanguageDictionary["IterationPath_Iteration"] + " {0:D2}", (int)devIteration);
                            }

                            IterationPathList.Add(iterationPathStr);

                            iterationPathStr = "$$PROJECTNAME$$\\" + iterationPathStr;
                            curMilestone.ExtendedData["__TFS_IterationPath"] = iterationPathStr;

                            if (typeFields.ContainsKey("iterationpath"))
                                AddFieldToXmlNode(newWorkItemNode, typeFields["iterationpath"], iterationPathStr);
                        }
                    }

                    #endregion

                    //Milestone type
                    object msTypeName;
                    string curMilestoneType;
                    if (curMilestone.ExtendedData.TryGetValue("TypeName", out msTypeName))
                        curMilestoneType = Helper.PrepareStringForTFS(msTypeName as string, Helper.TFS_MaxWorkItemTitleLen);
                    else
                        curMilestoneType = Helper.PrepareStringForTFS(curMilestone.Name, Helper.TFS_MaxWorkItemTitleLen);

                    //Add to type list
                    MilestoneTypes.Add(curMilestoneType);

                    //Write attribute if wanted
                    if (typeFields.ContainsKey("milestonetype"))
                        AddFieldToXmlNode(newWorkItemNode, typeFields["milestonetype"], curMilestoneType);

                    //Write predecessor / successor
                    int milestoneIndex = milestoneOrderList.IndexOfKey((int)curMilestone.ExtendedData["TFS_WI_ScheduleNumber"]);
                    if (typeFields.ContainsKey("milestonepredecessor"))
                    {
                        AddFieldToXmlNode(newWorkItemNode, typeFields["milestonepredecessor"],
                            milestoneIndex != 0 ? milestoneOrderList[milestoneOrderList.Keys[milestoneIndex - 1]] : string.Empty);
                    }
                    if (typeFields.ContainsKey("milestonesuccessor"))
                    {
                        AddFieldToXmlNode(newWorkItemNode, typeFields["milestonesuccessor"],
                            milestoneIndex != milestoneOrderList.Count - 1 ? milestoneOrderList[milestoneOrderList.Keys[milestoneIndex +1]] : string.Empty);
                    }

                    //Finish date
                    var finishDateStr = curMilestone.ScheduledDate.ToString("dd/MM/yy", CultureInfo.InvariantCulture);
                    curMilestone.ExtendedData["__TFS_FinishDate"] = finishDateStr;

                    if (typeFields.ContainsKey("milestonefinishdate"))
                        AddFieldToXmlNode(newWorkItemNode, typeFields["milestonefinishdate"], finishDateStr);

                    //Add new item to WI node
                    wiInstancesNode.AppendChild(newWorkItemNode);
                }
                catch (Exception e)
                {
                    TplProcessor.ToolProvider.ProcessLog.AddEntryAndThrowException(string.Format(Language.Error_Unexpected, e.Message, e.StackTrace));
                }
            }
        }

        /// <summary>
        /// Writes all default activity instances.
        /// </summary>
        private void WriteActivityInstances(XmlNode wiInstancesNode)
        {
            var typeName = GetWorkItemTypeName(ActivityTypeName);
            var typeFields = GetWorkItemTypeFields(ActivityTypeName);
            var typeConsts = GetWorkItemConstFields(ActivityTypeName);

            //Insert seperator comment
            wiInstancesNode.AppendChild(wiInstancesNode.OwnerDocument.CreateComment("Activities"));

            //Find artifact - milestone - activity connections and write activities
            foreach (IArtifact curArtifact in TplProcessor.ProcessProvider.Artifacts)
            {
                //Check whether artifact is initial
                if (!curArtifact.IsInitial)
                    continue;

                var belongingActivityAssociations = TplProcessor.ProcessProvider.GetAssociationsWithDestination<IActivity, IArtifact>(curArtifact);
                var belongingMilestoneAssociations = TplProcessor.ProcessProvider.GetAssociationsWithSource<IArtifact, IMilestone>(curArtifact);

                foreach (Association<IArtifact, IMilestone> curMilestoneLink in belongingMilestoneAssociations)
                {
                    foreach (Association<IActivity, IArtifact> curActivityLink in belongingActivityAssociations)
                    {
                        TplProcessor.ToolProvider.ProcessLog.AddEntry(string.Format(Language.WorkItems_WritingActivity, curActivityLink.Source.Name,
                                                                       curMilestoneLink.Destination.Name));

                        //Create default work item node
                        var newWorkItemNode = wiInstancesNode.OwnerDocument.CreateNode("element", "WI", string.Empty);

                        var typeAttribute = wiInstancesNode.OwnerDocument.CreateAttribute("type");
                        typeAttribute.Value = typeName;
                        newWorkItemNode.Attributes.Append(typeAttribute);
                        //--------------

                        //Write attributes
                        WriteCommonWIAttributes(curActivityLink.Source, newWorkItemNode, typeFields, typeConsts);

                        if (typeFields.ContainsKey("milestoneid"))
                            AddFieldToXmlNode(newWorkItemNode, typeFields["milestoneid"], curMilestoneLink.Destination.Id);

                        if (typeFields.ContainsKey("artifactid"))
                            AddFieldToXmlNode(newWorkItemNode, typeFields["artifactid"], curArtifact.Id);

                        WriteDisciplineAttribute(curArtifact, newWorkItemNode, typeFields);


                        //Write FinishDate/IterationPath and save them into the activity
                        object finishDate;
                        if (curMilestoneLink.Destination.ExtendedData.TryGetValue("__TFS_FinishDate", out finishDate))
                        {
                            var finishDateStr = finishDate as string;
                            curActivityLink.Source.ExtendedData["__TFS_FinishDate"] = finishDateStr;
                            if (typeFields.ContainsKey("activityfinishdate"))
                                AddFieldToXmlNode(newWorkItemNode, typeFields["activityfinishdate"], finishDateStr);
                        }

                        object iterationPath;
                        if (curMilestoneLink.Destination.ExtendedData.TryGetValue("__TFS_IterationPath", out iterationPath))
                        {
                            var iterationPathStr = iterationPath as string;
                            curActivityLink.Source.ExtendedData["__TFS_IterationPath"] = iterationPathStr;
                            if (typeFields.ContainsKey("iterationpath"))
                                AddFieldToXmlNode(newWorkItemNode, typeFields["iterationpath"], iterationPathStr);
                        }

                        wiInstancesNode.AppendChild(newWorkItemNode);
                    }
                }
            }
        }

        /// <summary>
        /// Writes all default task instances.
        /// </summary>
        private void WriteTaskInstances(XmlNode wiInstancesNode)
        {
            var typeName = GetWorkItemTypeName(TaskTypeName);
            var typeFields = GetWorkItemTypeFields(TaskTypeName);
            var typeConsts = GetWorkItemConstFields(TaskTypeName);

            //Insert seperator comment
            wiInstancesNode.AppendChild(wiInstancesNode.OwnerDocument.CreateComment("Tasks"));

            //Write tasks
            foreach (ITask curTask in TplProcessor.ProcessProvider.Tasks)
            {
                //Find product belonging to this task
                var taskActivityAssoc = TplProcessor.ProcessProvider.GetAssociationsWithSource<ITask, IActivity>(curTask);
                if (taskActivityAssoc.Length == 0)
                    continue;
                if (taskActivityAssoc.Length > 1)
                    TplProcessor.ToolProvider.ProcessLog.AddEntry("Ambiguous task-activity association.", LogEntryType.Warning);

                IActivity belongingActivity = ((Association<ITask, IActivity>)taskActivityAssoc[0]).Destination;

                var activityProductAssoc = TplProcessor.ProcessProvider.GetAssociationsWithSource<IActivity, IArtifact>(belongingActivity);
                if (activityProductAssoc.Length == 0)
                    continue;
                if (activityProductAssoc.Length > 1)
                    TplProcessor.ToolProvider.ProcessLog.AddEntry("Ambiguous activity-artifact association.", LogEntryType.Warning);

                IArtifact belongingArtifact = ((Association<IActivity, IArtifact>)activityProductAssoc[0]).Destination;
                //--------------------------------------

                //Only write initial tasks
                if (!belongingArtifact.IsInitial)
                    continue;

                TplProcessor.ToolProvider.ProcessLog.AddEntry(string.Format(Language.WorkItems_WritingTask, curTask.Name));

                //Create default work item node
                var newWorkItemNode = wiInstancesNode.OwnerDocument.CreateNode("element", "WI", string.Empty);

                var typeAttribute = wiInstancesNode.OwnerDocument.CreateAttribute("type");
                typeAttribute.Value = typeName;
                newWorkItemNode.Attributes.Append(typeAttribute);
                //--------------

                //Write attributes
                WriteCommonWIAttributes(curTask, newWorkItemNode, typeFields, typeConsts);

                if (typeFields.ContainsKey("activityid"))
                    AddFieldToXmlNode(newWorkItemNode, typeFields["activityid"], belongingActivity.Id);

                if (typeFields.ContainsKey("artifactid"))
                    AddFieldToXmlNode(newWorkItemNode, typeFields["artifactid"], belongingArtifact.Id);

                WriteDisciplineAttribute(belongingArtifact, newWorkItemNode, typeFields);

                //Write FinishDate/IterationPath
                if (typeFields.ContainsKey("taskfinishdate"))
                {
                    object finishDate;
                    if (belongingActivity.ExtendedData.TryGetValue("__TFS_FinishDate", out finishDate))
                        AddFieldToXmlNode(newWorkItemNode, typeFields["taskfinishdate"], finishDate as string);
                }

                if (typeFields.ContainsKey("iterationpath"))
                {
                    object iterationPath;
                    if (belongingActivity.ExtendedData.TryGetValue("__TFS_IterationPath", out iterationPath))
                        AddFieldToXmlNode(newWorkItemNode, typeFields["iterationpath"], iterationPath as string);
                }

                wiInstancesNode.AppendChild(newWorkItemNode);
            }
        }

        /// <summary>
        /// Writes all initial artifact instances.
        /// </summary>
        private void WriteArtifactInstances(XmlNode wiInstancesNode)
        {
            var typeName = GetWorkItemTypeName(ArtifactTypeName);
            var typeFields = GetWorkItemTypeFields(ArtifactTypeName);
            var typeConsts = GetWorkItemConstFields(ArtifactTypeName);

            //Insert some comment
            wiInstancesNode.AppendChild(wiInstancesNode.OwnerDocument.CreateComment("Initial artifacts"));

            //Write
            foreach (IArtifact curArtifact in TplProcessor.ProcessProvider.Artifacts)
            {
                if (curArtifact.IsInitial)
                {
                    try
                    {
                        TplProcessor.ToolProvider.ProcessLog.AddEntry(string.Format(Language.WorkItems_WritingProduct, curArtifact.Name));

                        //Create default work item node
                        var newWorkItemNode = wiInstancesNode.OwnerDocument.CreateNode("element", "WI", string.Empty);

                        var typeAttribute = wiInstancesNode.OwnerDocument.CreateAttribute("type");
                        typeAttribute.Value = typeName;
                        newWorkItemNode.Attributes.Append(typeAttribute);
                        //--------------

                        //Write attributes
                        WriteCommonWIAttributes(curArtifact, newWorkItemNode, typeFields, typeConsts);

                        WriteDisciplineAttribute(curArtifact, newWorkItemNode, typeFields);

                        if (typeFields.ContainsKey("artifacttype"))
                        AddFieldToXmlNode(newWorkItemNode, typeFields["artifacttype"], Helper.PrepareStringForTFS(curArtifact.TypeName, Helper.TFS_MaxWorkItemTitleLen));

                        wiInstancesNode.AppendChild(newWorkItemNode);
                    }
                    catch (Exception e)
                    {
                        TplProcessor.ToolProvider.ProcessLog.AddEntryAndThrowException(string.Format(Language.Error_Unexpected, e.Message, e.StackTrace));
                    }
                }
            }
        }


        /// <summary>
        /// Writes a subset of the attributes id, name and description as well as all constant fields for a given work item.
        /// </summary>
        private static void WriteCommonWIAttributes(IProcessElement wiElement, XmlNode wiNode, Dictionary<string, string> wiFields, Dictionary<string, string> wiConstFields)
        {
            //Name
            if (wiFields.ContainsKey("name"))
                AddFieldToXmlNode(wiNode, wiFields["name"], Helper.PrepareStringForTFS(wiElement.Name, Helper.TFS_MaxWorkItemTitleLen));

            //Description
            if (wiFields.ContainsKey("description"))
                AddFieldToXmlNode(wiNode, wiFields["description"], Helper.PrepareStringForTFS(wiElement.Description, Helper.TFS_MaxWorkItemTextLen));

            //Id
            if (wiFields.ContainsKey("id"))
                AddFieldToXmlNode(wiNode, wiFields["id"], wiElement.Id);

            //Constant fields
            foreach (var curConst in wiConstFields)
                AddFieldToXmlNode(wiNode, curConst.Key, curConst.Value);
        }

        /// <summary>
        /// Writes the discipline attribute for an artifact related to a work item.
        /// </summary>
        private void WriteDisciplineAttribute(IArtifact artifact, XmlNode wiNode, Dictionary<string, string> wiFields)
        {
            if (wiFields.ContainsKey("artifactdiscipline"))
            {
                //Find belonging discipline
                var artifactDisciplineAssoc = TplProcessor.ProcessProvider.GetAssociationsWithSource<IArtifact, IDiscipline>(artifact);
                if (artifactDisciplineAssoc.Length == 0)
                    TplProcessor.ToolProvider.ProcessLog.AddEntry("Product without discipline.", LogEntryType.Warning);
                else
                {
                    if (artifactDisciplineAssoc.Length > 1)
                        TplProcessor.ToolProvider.ProcessLog.AddEntry("Ambiguous product-discipline association.", LogEntryType.Warning);

                    //Write discipline field
                    AddFieldToXmlNode(wiNode, wiFields["artifactdiscipline"], ((Association<IArtifact, IDiscipline>)artifactDisciplineAssoc[0]).Destination.Name);
                }
            }
        }

        /// <summary>
        /// Returns the user-defined type name for a given work item type.
        /// </summary>
        private string GetWorkItemTypeName(string type)
        {
            var typeNode = TplProcessor.TemplateConfigDocument.SelectSingleNode("//Tasks.WorkItemTracking/Types/Type[@type='" + type + "']");
            if (typeNode == null)
                return null;
            var typeNameAttr = typeNode.Attributes.GetNamedItem("name");
            if(typeNameAttr == null)
                throw new WorkItemConversionException(string.Format(Language.Error_NotFound_InvalidTemplate, "Template config: //Tasks.WorkItemTracking/Types/Type/@name"));
            return typeNameAttr.Value;
        }

        /// <summary>
        /// Returns the user-defined fields for a given work item type.
        /// </summary>
        private Dictionary<string, string> GetWorkItemTypeFields(string type)
        {
            var fieldsDict = new Dictionary<string, string>();

            var fieldNodes = TplProcessor.TemplateConfigDocument.SelectNodes("//Tasks.WorkItemTracking/Types/Type[@type='" + type + "']/Field");
            foreach (XmlNode curFieldNode in fieldNodes)
            {
                var fieldTypeAttr = curFieldNode.Attributes.GetNamedItem("type");
                var fieldNameAttr = curFieldNode.Attributes.GetNamedItem("name");
                if (fieldTypeAttr != null && fieldNameAttr != null)
                {
                    string fieldName = fieldTypeAttr.Value.ToLower();
                    if (fieldName.Equals(TypeFieldConst))
                        continue; //Constant fields are handled separately

                    if (!fieldsDict.ContainsKey(fieldName))
                        fieldsDict.Add(fieldName, fieldNameAttr.Value);
                    else
                        TplProcessor.ToolProvider.ProcessLog.AddEntry(Language.Error_InvalidWorkItemTypeField, LogEntryType.Warning);
                }
                else
                    TplProcessor.ToolProvider.ProcessLog.AddEntry(Language.Error_InvalidWorkItemTypeField, LogEntryType.Warning);
            }
            return fieldsDict;
        }

        /// <summary>
        /// Returns the names and values of constant fields for a given work item type.
        /// </summary>
        private Dictionary<string, string> GetWorkItemConstFields(string type)
        {
            var constDict = new Dictionary<string, string>();

            var constNodes = TplProcessor.TemplateConfigDocument.SelectNodes("//Tasks.WorkItemTracking/Types/Type[@type='" + type + "']/Field[@type='const']");
            foreach (XmlNode curConstNode in constNodes)
            {
                var fieldNameAttr = curConstNode.Attributes.GetNamedItem("name");
                if (fieldNameAttr == null)
                {
                    TplProcessor.ToolProvider.ProcessLog.AddEntry(Language.Error_InvalidWorkItemTypeField, LogEntryType.Warning);
                    continue;
                }

                var fieldValueNode = curConstNode.SelectSingleNode("value");
                constDict.Add(fieldNameAttr.Value, fieldValueNode != null ? fieldValueNode.InnerText : string.Empty);
            }
            return constDict;
        }


        /// <summary>
        /// Modifies work item type definition files according to the template configuration.
        /// </summary>
        private void ModifyTypeDefinitionFiles()
        {
            var typeModificationNodes = TplProcessor.TemplateConfigDocument.SelectNodes("//Tasks.WorkItemTracking/TypeModifications/Modification");
            foreach (XmlNode curModificationNode in typeModificationNodes)
            {
                var typeAttr = curModificationNode.Attributes.GetNamedItem("type");
                var folderAttr = curModificationNode.Attributes.GetNamedItem("folder");
                var fileAttr = curModificationNode.Attributes.GetNamedItem("file");

                if (typeAttr == null || folderAttr == null || fileAttr == null)
                    throw new WorkItemConversionException(Language.Error_InvalidWorkItemTypeModification);

                //Add log message
                TplProcessor.ToolProvider.ProcessLog.AddEntry(string.Format(Language.Processing_TypeDefinition, fileAttr.Value));


                var typeDefinitionFileName = TplProcessor.ToolProvider.OutputPath + "/" + folderAttr.Value + "/" + fileAttr.Value;
                #region Load type definition template
                var typeDefinitionDoc = new XmlDocument();
                try
                {
                    typeDefinitionDoc.Load(typeDefinitionFileName);
                }
                catch
                {
                    throw new WorkItemConversionException(string.Format(Language.Error_LoadingFailed, typeDefinitionFileName));
                }
                #endregion
                
                //'Dispatch' processing
                switch(typeAttr.Value.ToLower())
                {
                    case "allowedvalues":
                        ModifyTypeDefinitionFile_AllowedValues(curModificationNode, typeDefinitionDoc);
                        break;

                    default:
                        TplProcessor.ToolProvider.ProcessLog.AddEntry(string.Format(Language.Error_InvalidModificationTypeInWorkItemTypeDefinition, typeAttr.Value), LogEntryType.Warning);
                        break;
                }

                #region Save type definition file
                try
                {
                    typeDefinitionDoc.Save(typeDefinitionFileName);
                }
                catch
                {
                    throw new WorkItemConversionException(string.Format(Language.Error_SavingFailed, typeDefinitionFileName));
                }
                #endregion
            }
        }

        /// <summary>
        /// Helper method for ModifyTypeDefinitionFiles.
        /// Modifies a AllowedValues field in a work item type definition file.
        /// </summary>
        private void ModifyTypeDefinitionFile_AllowedValues(XmlNode modificationNode, XmlDocument definitionDocument)
        {
            var fieldNode = modificationNode.SelectSingleNode("Field");
            var valuesNode = modificationNode.SelectSingleNode("Values");

            if(fieldNode == null || valuesNode == null)
                throw new WorkItemConversionException(Language.Error_InvalidWorkItemTypeModification);

            //Get field values node to be modified
            var fieldValuesNode = definitionDocument.SelectSingleNode("/WITD/WORKITEMTYPE/FIELDS/FIELD[@refname='" + fieldNode.InnerText + "']/ALLOWEDVALUES");
            if (fieldValuesNode == null)
                throw new WorkItemConversionException(Language.Error_InvalidWorkItemTypeModification);

            //'Dispatch' processing
            switch(valuesNode.InnerText.ToLower())
            {
                case "allartifactnames":
                    ModifyTypeDefinitionFile_AllowedValues_AllArtifactNames(fieldValuesNode);
                    break;

                case "alldisciplinenames":
                    ModifyTypeDefinitionFile_AllowedValues_AllDisciplineNames(fieldValuesNode);
                    break;

                case "allmilestonetypes":
                    ModifyTypeDefinitionFile_AllowedValues_AllMilestoneTypes(fieldValuesNode);
                    break;

                default:
                    TplProcessor.ToolProvider.ProcessLog.AddEntry(string.Format(Language.Error_InvalidValueTypeInWorkItemTypeDefinition, valuesNode.InnerText), LogEntryType.Warning);
                    break;
            }
        }

        /// <summary>
        /// Helper method for ModifyTypeDefinitionFile_AllowedValues.
        /// Responsible for the 'Values' value AllArtifactNames.
        /// </summary>
        private void ModifyTypeDefinitionFile_AllowedValues_AllArtifactNames(XmlNode fieldValuesNode)
        {
            var alreadyWrittenProducts = new HashSet<string>(); //Set to avoid writing the same artifact name more than once

            foreach (IArtifact curArtifact in TplProcessor.ProcessProvider.Artifacts)
            {
                if (alreadyWrittenProducts.Add(curArtifact.Name))
                {
                    var newTypeNode = fieldValuesNode.OwnerDocument.CreateNode("element", "LISTITEM", "");
                    var valueAttribute = fieldValuesNode.OwnerDocument.CreateAttribute("value");
                    valueAttribute.Value = curArtifact.Name;
                    newTypeNode.Attributes.Append(valueAttribute);
                    fieldValuesNode.AppendChild(newTypeNode);
                }
            }
        }

        /// <summary>
        /// Helper method for ModifyTypeDefinitionFile_AllowedValues.
        /// Responsible for the 'Values' value AllDisciplineNames.
        /// </summary>
        private void ModifyTypeDefinitionFile_AllowedValues_AllDisciplineNames(XmlNode fieldValuesNode)
        {
            var alreadyWrittenDisciplines = new HashSet<string>();

            foreach (IDiscipline curDiscipline in TplProcessor.ProcessProvider.Disciplines)
            {
                if (alreadyWrittenDisciplines.Add(curDiscipline.Name))
                {
                    var newTypeNode = fieldValuesNode.OwnerDocument.CreateNode("element", "LISTITEM", "");
                    var valueAttribute = fieldValuesNode.OwnerDocument.CreateAttribute("value");
                    valueAttribute.Value = curDiscipline.Name;
                    newTypeNode.Attributes.Append(valueAttribute);
                    fieldValuesNode.AppendChild(newTypeNode);
                }
            }
        }

        /// <summary>
        /// Helper method for ModifyTypeDefinitionFile_AllowedValues.
        /// Responsible for the 'Values' value AllMilestoneTypes.
        /// </summary>
        private void ModifyTypeDefinitionFile_AllowedValues_AllMilestoneTypes(XmlNode fieldValuesNode)
        {
            foreach (string curMilestoneType in MilestoneTypes)
            {
                var newTypeNode = fieldValuesNode.OwnerDocument.CreateNode("element", "LISTITEM", "");
                var valueAttribute = fieldValuesNode.OwnerDocument.CreateAttribute("value");
                valueAttribute.Value = curMilestoneType;
                newTypeNode.Attributes.Append(valueAttribute);
                fieldValuesNode.AppendChild(newTypeNode);
            }
        }


        public string[] GetIterationPaths()
        {
            return IterationPathList.ToArray();
        }

        /// <summary>
        /// Internal helper class to propagate errors to the process method.
        /// </summary>
        class WorkItemConversionException : TFSException
        {
            public WorkItemConversionException(string error)
                : base(error)
            {
            }
        }
    }
}